package Model;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.swing.JLabel;

import View.Display;

public class MonteCarlo implements Runnable{

	final int numberOfCores = Runtime.getRuntime().availableProcessors();
	final double blockingCoefficient = 0.9;
	int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));

	String CallPutFlag;
	Double S;
	Double X;
	Double r;
	Double T;
	Double b;
	Double v;
	int nSteps;
	int nSimulations;
	Double MonteCarloStandardOption = 0.0;
	Display update;
	long start, end;
	private ExecutorService executor;
	ArrayList<Callable<Double>> partitions;

	public MonteCarlo(String CallPutFlag, Double S, Double X, Double r, Double T, Double b, Double v, int nSteps, int nSimulations, Display display, int poolsize) {
		if(poolsize!=0)this.poolSize=poolsize;
		update = display;
		System.out.println(S + " " + X + " " + T + " " + r + " " + b + " " + v + " " + nSteps + " " + nSimulations);
		this.CallPutFlag = CallPutFlag;
		this.S = S;
		this.X = X;
		this.T = T;
		this.r = r;
		this.b = b;
		this.v = v;
		this.nSteps = nSteps;
		this.nSimulations = nSimulations;


		partitions = new ArrayList<Callable<Double>>();

	}

	public void calculate() throws ExecutionException {
		executor=Executors.newFixedThreadPool(poolSize);
		start = System.currentTimeMillis();
		System.out.println(S + " " + X + " " + T + " " + r + " " + b + " " + v
				+ " " + nSteps + " " + nSimulations);
		Double dt, St;
		Double Sum = 0.0, Drift, vSqrdt;

		int i=0, j, z = 0;
		dt = T / nSteps;
		Drift = (b - Math.pow(v, 2) / 2) * dt;
		vSqrdt = v * Math.sqrt(dt);
		if (CallPutFlag.equals("c")) {
			z = 1;
		} else if (CallPutFlag.equals("p")) {
			z = -1;
		}
		St = S;

		for (i = 1; i <= nSimulations; i++) {

			
			partitions.add(new MyCallable(nSteps,Drift,St,S,vSqrdt,z,X));

		}
		i=0;
		try {
			List<Future<Double>> allSimulations = executor.invokeAll(partitions, 10000, TimeUnit.SECONDS);
			poolSize=Thread.activeCount();
			for(Future<Double> currentsimulation : allSimulations) { 
				Sum += currentsimulation.get(); 
				update.setBar(i); 
				i++;
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		executor.shutdown();
		

		
		System.out.println(Sum);
		end = System.currentTimeMillis();
		update.setExecution();
		MonteCarloStandardOption = Math.exp(-r * T) * (Sum / nSimulations);
		
		
		
		update.setResult(100000);
		System.out.println(MonteCarloStandardOption);

	}

	public int getPoolSize() {
		return poolSize;
	}
	
	public void setPoolSize(int size) {
		poolSize=size;
	}

	public Double getMonteCarloStandardOption() {
		return MonteCarloStandardOption;
	}

	public void setMonteCarloStandardOption(Double monteCarloStandardOption) {
		MonteCarloStandardOption = monteCarloStandardOption;
	}

	public long getStart() {
		return start;
	}

	public void setStart(long start) {
		this.start = start;
	}

	public long getEnd() {
		return end;
	}

	public void setEnd(long end) {
		this.end = end;
	}

	@Override
	public void run() {
		try {
			calculate();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
